#include <adwin/ADwinSystem.h>
#include <adwin/ADwinSystemException.h>
#include <adwin/FParameter.h>
#include <adwin/Tools.h>
#include <src/ADwin_V2.h>

namespace ADwin {
namespace Driver {
struct FParameter::Impl {
  Impl(ADwinSystem* adwinSystem, int parameterNumber)
      : _adwinSystem(adwinSystem), _parameterNumber(parameterNumber) {}

  double valueAsDouble() const;
  void setValueAsDouble(double value);
  float valueAsSingle() const;
  void setValueAsSingle(float value);

 private:
  ADwinSystem* _adwinSystem;
  int _parameterNumber;
};

double FParameter::Impl::valueAsDouble() const {
  auto exception = Tools::CheckParameterNumber(_parameterNumber);
  if (exception != nullptr) {
    throw exception;
  }

  long errorNumber = 0;
  double fParDouble = Get_FPar_Double(
      _parameterNumber, _adwinSystem->GetDeviceNumber(), &errorNumber);

  if (errorNumber != 0) {
    throw ADwinSystemException(errorNumber);
  }
  return fParDouble;
}

void FParameter::Impl::setValueAsDouble(double value) {
  auto exception = Tools::CheckParameterNumber(_parameterNumber);
  if (exception != nullptr) {
    throw exception;
  }

  long errorNumber = 0;
  Set_FPar_Double(_parameterNumber, value, _adwinSystem->GetDeviceNumber(),
                  &errorNumber);

  if (errorNumber != 0) {
    throw ADwinSystemException(errorNumber);
  }
}

float FParameter::Impl::valueAsSingle() const {
  auto exception = Tools::CheckParameterNumber(_parameterNumber);
  if (exception != nullptr) {
    throw exception;
  }

  long errorNumber = 0;
  float fPar =
      Get_FPar(_parameterNumber, _adwinSystem->GetDeviceNumber(), &errorNumber);

  if (errorNumber != 0) {
    throw ADwinSystemException(errorNumber);
  }
  return fPar;
}

void FParameter::Impl::setValueAsSingle(float value) {
  auto exception = Tools::CheckParameterNumber(_parameterNumber);
  if (exception != nullptr) {
    throw exception;
  }

  long errorNumber = 0;
  Set_FPar(_parameterNumber, value, _adwinSystem->GetDeviceNumber(),
           &errorNumber);

  if (errorNumber != 0) {
    throw ADwinSystemException(errorNumber);
  }
}

FParameter::FParameter(ADwinSystem* adwinSystem, int parameterNumber)
    : _impl(new Impl(adwinSystem, parameterNumber)) {}
FParameter::~FParameter() = default;
double FParameter::valueAsDouble() const { return _impl->valueAsDouble(); }
void FParameter::setValueAsDouble(double value) {
  _impl->setValueAsDouble(value);
}
float FParameter::valueAsSingle() const { return _impl->valueAsSingle(); }
void FParameter::setValueAsSingle(float value) {
  _impl->setValueAsSingle(value);
}
}  // namespace Driver
}  // namespace ADwin